//
// Created by vanweilin on 2022/9/4 19:21.
//

#ifndef LEARN_OPENCV_SIMPLEIMGREAD_HPP
#define LEARN_OPENCV_SIMPLEIMGREAD_HPP

#include "../macros_deal.hpp"
#include "../collection.h"
#include "../io_util.hpp"
#include "opencv2/opencv.hpp"

namespace SimpleImgHandle {
    using namespace TupleHandleUtil;
    using namespace cv;
    using namespace IoUtil;

    namespace InputHelper {
        static const char *getInput(const char *descp, int len = 500) {
            char *input = new char[len];
            printLn(descp);
            cin.getline(input, len);
            return const_cast<const char *>( input);
        }
    }

    namespace SizeDescpHelper {
        string toBytes(size_t byte_size) {
            return to_string(byte_size).append("bytes");
        }

        string toKb(size_t byte_size) {
            return to_string(byte_size / 1024.0).append("KB");
        }

        string toMb(size_t byte_size) {
            return to_string(byte_size / 1024.0 / 1024.0).append("MB");
        }

        string toGb(size_t byte_size) {
            return to_string(byte_size / 1024.0 / 1024.0 / 1024.0).append("GB");
        }

#define RScalar(R, G, B, ...) \
        Scalar(B,G,R,##__VA_ARGS__)

    }


    class MatList {
        vector<Mat> matVector;
        vector<tuple<string, string, string>> fileSizeInfo;
        size_t totalSize = 0;
    public:
        explicit MatList(size_t len) {
            matVector.reserve(len);
            fileSizeInfo.reserve(len);
        }

        MatList() = default;

    private:
        static Mat readFromFile(const string &fileName) {
            return move(imread(fileName));
        }

        static inline void doHandleMat(MatList &matList, Mat &&mat, const string &fileName) {
            if (!mat.empty()) {
                /*此处获取到的是进入内存后需要处理的总大小*/
                auto mat_size = mat.total() * mat.elemSize1();
                matList.matVector.push_back(mat);
                matList.fileSizeInfo.emplace_back(fileName, STRING(mat.size),
                                                  STRING(SizeDescpHelper::toMb(mat_size)));
                matList.totalSize += mat_size;
            }
        }

    public:
        const vector<Mat> &getMatVector() const {
            return matVector;
        }

        const vector<tuple<string, string, string>> &getFileSizeInfo() const {
            return fileSizeInfo;
        }

        /*同步读取，减少时间消耗*/
        static MatList readAllFromFiles(const vector<string> &fileNames) {
            MatList matList(fileNames.size());
            for (const auto &item: fileNames) {
                doHandleMat(matList, readFromFile(item), item);
            }
            return matList;
        }

        /*异步读取，减少时间消耗*/
        static MatList readAllFromFilesAsync(const vector<string> &fileNames) {
            MatList matList(fileNames.size());
            ThreadPool::SimplePool pool;
            mutex mut;
            for (const auto &item: fileNames) {
                pool.commit([&matList, &mut, &item] {
                    auto img = readFromFile(item);
                    lock_guard<mutex> ol{mut};
                    doHandleMat(matList, move(img), item);
                });
            }
            return matList;
        }

        static MatList readAllFromDirAsync(const string &fileName) {
            return readAllFromFilesAsync(DirFiles(fileName));
        }

        static MatList readAllFromDir(const string &fileName) {
            return readAllFromFiles(DirFiles(fileName));
        }


        friend ostream &operator<<(ostream &os, const MatList &list) {
            return os << STRING("matList_info: ", list.fileSizeInfo, "\n", "totalSize is: ",
                                SizeDescpHelper::toMb(list.totalSize));
        }

    };

    class MatShow {
        const char *title;
        const Mat &img;


    public:/*构造函数*/

        MatShow(const char *title, Mat &&img) : title(title), img(img) {}

        MatShow(const char *title, const Mat &img) : title(title), img(img) {}

        MatShow(const pair<const char *, const Mat &> pair) : title(pair.first), img(pair.second) {}

        virtual ~MatShow() {
            cv::waitKey();
            cv::destroyWindow(title);
        }

    public:/*结合windows扩展方法*/

        const MatShow &withWindows(const tuple<int, int, int, int> &tp = make_tuple(0, 0, 0, 0)) {
            namedWindow(title);
            moveWindow(title, get<0>(tp), get<1>(tp));
            resizeWindow(title, Size(get<2>(tp) == 0 ? img.cols : get<2>(tp), get<3>(tp) == 0 ? img.rows : get<3>(tp)));
            return *this;
        }

        /*设置滑动条
         * 1 滑动条名称
         * 2 最大阈值
         * 3 tuple参数（1 处理函数，2 pos保留值）*/
        template<class F, class ...ARGS>
        const MatShow &regTarBar(const char *barName,
                                 int maxValue,
                                 tuple<F, ARGS...> *spt
        ) const {
            cv::createTrackbar(barName, title, NULL, maxValue,
                               [](int pos, void *data) {
                                   auto *dp = (tuple<F, ARGS...> *) data;
                                   TUPLE2FUNC_DEAL(1,
                                                   ([dp, pos](ARGS &&... args) {
                                                       get<0>(*dp)(pos, args...);
                                                   }), *dp);
                               }, spt);
            return *this;
        }

        template<class F, class ...ARGS>
        const MatShow &regMouseAction(tuple<F, ARGS...> *spt) const {
            cv::setMouseCallback(title, [](int event, int x, int y, int flag, void *data) {
                auto *dp = (tuple<F, ARGS...> *) data;
                TUPLE2FUNC_DEAL(1,
                                ([dp, event, x, y, flag](ARGS &&... args) {
                                    get<0>(*dp)(event, x, y, flag, args...);
                                }), *dp);
            }, spt);
            return *this;
        }

        const MatShow &regKeyAction() const {

            return *this;
        }

    public:/*基础方法*/
        const Mat &getImgInfo() const {
            return img;
        }

        const char *getTitle() const {
            return title;
        }


        void show() const {
            imshow(title, img);
        }

        void show(const Mat &mat) const {
            imshow(title, mat);
        }

    };

#define WithName(arg) \
        #arg,arg


}
//   SimpleImgHandle::MatShow::withName(#arg,arg)

#endif //LEARN_OPENCV_SIMPLEIMGREAD_HPP
